package com.example.android.raycast;

import java.io.IOException;
import java.io.InputStream;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;

/***
 * Stores data about a monster in the game
 */
public class Monster {
	
	//Whether it has been mSeen by the current raycasting loop
	private boolean mSeen = false;
	
	private int mHealth = 100;
	private Bitmap[] mBitmaps;
	private final int mNumberOfFrames = 3;
	private int mCurrentAnimationFrame = 0;
	private final int mHealthBitmapIndex = 3;
	private final int mAmmoBitmapIndex = 4;
	private final int mLampBitmapIndex = 5;
	//1000 is some value greater than the canvas width
	
	//we need to store these for the X ray and the Y ray
	private int mSeenAtFirstColumn = 1000;
	private int mSeenAtLastColumn = 0;
	private float mSeenAtDistance = Float.MAX_VALUE;
	private int mTextureStartOffset = 1000;
	private int mTextureLastOffset = 0;
		
	private float mXRaySeenAtDistance = Float.MAX_VALUE;
	private float mYRaySeenAtDistance = Float.MAX_VALUE;
	
	private int mItemType = 0;
	private int TEXTURE_WIDTH = 64;
	private int TEXTURE_HEIGHT = 64;
	
	private int MONSTER_HEIGHT = 48;
	private int MONSTER_WIDTH = 48;
	private int PICKUP_WIDTH = 24;
	private int PICKUP_HEIGHT = 24;	

	private int mDamage = 5;	
	private int mX = 0;
	private int mY = 0;
	private int mDirectionAngle = 0;
	private int mMovementSpeed = 8;
	private int mStartX = 0;
	private int mStartY = 0;
	private int mStartAngle = 0;
		
    private static BitmapFactory.Options sBitmapOptions 
    = new BitmapFactory.Options();
	
	public Monster(int type, int X, int Y, int angle, Context context) {
		sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
		
		//note this is actual position, not grid position
		mX = X;
		mY = Y;		
		mDirectionAngle = angle;
		
		mStartX = X;
		mStartY = Y;
		mStartAngle = angle;
		
		mHealth = 100;
		
		//The number in the world map array used for this monster (eg 18)
		mItemType = type;
		
		mBitmaps = new Bitmap[6];
		mBitmaps[0] = loadBitmap(context, R.drawable.skate1);
		mBitmaps[1] = loadBitmap(context, R.drawable.skate2);
		mBitmaps[2] = loadBitmap(context, R.drawable.skate3);
		
		//Extra bitmaps for sprites
		mBitmaps[mHealthBitmapIndex] = loadBitmap(context, R.drawable.health48);
		mBitmaps[mAmmoBitmapIndex] = loadBitmap(context, R.drawable.ammo48);
		mBitmaps[mLampBitmapIndex] = loadBitmap(context, R.drawable.lamp);
		
	}
	
	
	public boolean isMonster() {
		if (mItemType >= Level.MONSTER_START && mItemType <= Level.MONSTER_END) {
			return true;
		} else {
			return false;			
		}
	}
	
	public boolean isSprite() {
		if (mItemType >= Level.SPRITE_START && mItemType <= Level.SPRITE_END) {
			return true;
		} else {
			return false;			
		}
	}
	
	public boolean isMovableItem() {
		//Technically there is no reason why health or ammo items could also be moved,
		//but we dont want to
		if (mItemType >= Level.MONSTER_START && mItemType <= Level.MONSTER_END) {
			return true;
		} else {
			return false;			
		}
	}
	
	public boolean isPickUpItem() {
		//Dont pick up objects such as lamps
		if (mItemType == Level.AMMO_ITEM || mItemType == Level.HEALTH_ITEM) {
			return true;
		} else {
			return false;			
		}
	}
	
	public boolean canWalkThroughItem() {
		//Cant walk through a monster, but allow walk over of lamp etc
		//TODO add barrels etc that player cant walk through
		if (mItemType >= Level.MONSTER_START && mItemType <= Level.MONSTER_END) {
			return false;
		} else {
			return true;			
		}
	}
	
	public int getX() {
		return mX;
	}
	
	public int getY() {
		return mY;
	}
	
	public int getType() {
		return mItemType;
	}
	
	public int getSpeed() {
		return mMovementSpeed;
	}
	
	public int getDirectionAngle() {
		return mDirectionAngle;
	}
	
	public void setDirectionAngle(int newAngle) {
		mDirectionAngle = newAngle;
	}
	
	public void updatePosition(int X, int Y) {
		mX = X;
		mY = Y;
	}
	
	public int getSeenAtFirstColumn() {
		return mSeenAtFirstColumn;
	}
	
	public int getSeenAtLastColumn() {
		return mSeenAtLastColumn;
	}
	
	public Bitmap getCurrentBitmap() {
		
		if(isMonster()) {		
			return mBitmaps[mCurrentAnimationFrame];
		} else if (mItemType == Level.HEALTH_ITEM) {
			return mBitmaps[mHealthBitmapIndex];
		} else if (mItemType == Level.AMMO_ITEM) {
			return mBitmaps[mAmmoBitmapIndex];
		} else { //(mItemType == Level.LAMP_ITEM)
			return mBitmaps[mLampBitmapIndex];
		} 
	}
	
	public int getDamage() {
		return mDamage;
	}
	public float getXRayDistance() {
		return mXRaySeenAtDistance;
	}
	
	public float getYRayDistance() {
		return mYRaySeenAtDistance;
	}
	
	public int getHeight() {
		if(isPickUpItem()) {
			return PICKUP_HEIGHT;
		} else {
			return MONSTER_HEIGHT;
		}
	}
	
	public int getWidth() {
		if(isPickUpItem()) {
			return PICKUP_WIDTH;
		} else {
			return MONSTER_WIDTH;
		}
	}
	
	public void resetSeen() {
		mSeen = false;
		mSeenAtFirstColumn = 1000;
		mSeenAtLastColumn = 0;
		mSeenAtDistance = Float.MAX_VALUE;
		mXRaySeenAtDistance = Float.MAX_VALUE;
		mYRaySeenAtDistance = Float.MAX_VALUE;		
		mTextureStartOffset = 1000;
		mTextureLastOffset = 0;
	}
	
	public void destroy() {
        for (int x = 0; x < mBitmaps.length; x++) {
        	if(mBitmaps[x] != null) {
        		mBitmaps[x].recycle();
        		mBitmaps[x] = null;
        	}
        }
	}
	
	public Rect getSourceRect() {
		//eg 0..29 - we should see the right side of the sprite, not the left!
		if(mTextureStartOffset == 0 && mTextureLastOffset < TEXTURE_WIDTH)
		{
			mTextureStartOffset = TEXTURE_WIDTH - mTextureLastOffset;
			mTextureLastOffset = TEXTURE_WIDTH;
			
		}
		//eg 30..63 - we should see the left side of the sprite, not the right
		if(mTextureLastOffset == TEXTURE_WIDTH-1 && mTextureStartOffset > 0)
		{					
			mTextureLastOffset = TEXTURE_WIDTH - mTextureStartOffset;
			mTextureStartOffset = 0;					
		}

		//left top right bottom
		//monster texture width == texture height
		return new Rect(mTextureStartOffset,0,mTextureLastOffset,TEXTURE_HEIGHT);
	}
	
	public int getHealth() {
		return mHealth;
	}
	
	public void setSeenData(int castColumn, float distToGridBeingHit, float intersection) {
		if(castColumn < mSeenAtFirstColumn) {
			mSeenAtFirstColumn = castColumn;
		}
		if(castColumn > mSeenAtLastColumn) {
			mSeenAtLastColumn = castColumn;
		}
		if(distToGridBeingHit < mSeenAtDistance) {
			mSeenAtDistance = distToGridBeingHit;
		}
		int textureOffset = (int) (intersection % TEXTURE_WIDTH);
		if(textureOffset < mTextureStartOffset) {
			mTextureStartOffset = textureOffset;
		}
		if(textureOffset > mTextureLastOffset) {
			mTextureLastOffset = textureOffset;
		}		
	}

	
	
	//Monster has been shot, reduce its mHealth
	public void hit(Weapon weapon, int newAngle) {
		mHealth -= weapon.getFullHitDamage();
		mMovementSpeed += 2;
		mDirectionAngle = newAngle;
	}
	
    protected Bitmap loadBitmap(Context context, int resourceId) {
        Bitmap bitmap = null;
        if (context != null) {
          
            InputStream is = context.getResources().openRawResource(resourceId);
            try {
                bitmap = BitmapFactory.decodeStream(is, null, sBitmapOptions);
            } finally {
                try {
                    is.close();
                } catch (IOException e) {
                    // Ignore.
                }
            }
        }

        return bitmap;
    }

	public void animate() {
		if(isMonster() && mHealth > 0) {
			mCurrentAnimationFrame++;
			//Cycle between the frames
			if(mCurrentAnimationFrame > (mNumberOfFrames-1)) {
				mCurrentAnimationFrame = 0;
			}
		}
		
	}

	public boolean getSeen() {
		return mSeen;
	}

	public float getDistance() {
		return mSeenAtDistance;
	}

	public void hasBeenSeen() {
		mSeen = true;		
	}

	public void updateYDistance(float distToVerticalGridBeingHit) {
		if(distToVerticalGridBeingHit < mYRaySeenAtDistance) {
			mYRaySeenAtDistance = distToVerticalGridBeingHit;
		}
		
	}

	public void updateXDistance(float distToHorizontalGridBeingHit) {
		if(distToHorizontalGridBeingHit < mXRaySeenAtDistance) {
			mXRaySeenAtDistance = distToHorizontalGridBeingHit;
		}
		
	}

	public void partialHit(Weapon weapon, int newAngle) {
		mHealth -= weapon.getPartialHitDamage();
		mMovementSpeed += 2;
		mDirectionAngle = newAngle;
	}

	public void reset() {
		mHealth = 100;
		mCurrentAnimationFrame = 0;
		mX = mStartX;
		mY = mStartY;
		mDirectionAngle = mStartAngle;
		resetSeen();
		
	}
	
}
